﻿using System;
using System.Net;

using System.Linq;
using System.Collections.Generic;
namespace AudioTester.WaveStreams
{

    public enum Channels
    {
        Left = 1,
        Right = 2,
        Both = 0
    }
    public delegate double WaveFunction(ChannelContext context);
    public class ChannelContext
    {
        public double currentPosition = 0;
        public double lastValue = 0;
        public WaveHeader header = null;

    }
    public static class DataGenerators
    {
        public static IEnumerable<Byte> CreateWhiteNoiseGenerator(this WaveHeader header ,Channels channels)
        {
            Random rnd = new Random();
            WaveFunction func = context =>
            {

                return rnd.NextDouble() - 0.5 * 2;

            };

            return GetFunctionGenerator(header, func, channels);


        }




        public static IEnumerable<Byte> CreateSinGenerator(this WaveHeader header, int Hz, Channels channels)
        {

            WaveFunction func = context =>
                {
                    var inputSin = (context.currentPosition * 2 * Math.PI) * Hz;
                    return Math.Sin(inputSin);

                };

            return GetFunctionGenerator(header, func ,channels);

        }


        public static IEnumerable<Byte> CreateTriangularWaveGenerator(this WaveHeader header, int Hz, Channels channels)
        {
            WaveFunction func = context =>
            {
                var inputSin = context.currentPosition * Hz;
                return TriangularWave(inputSin);

            };

            return GetFunctionGenerator(header, func,channels);
        }

        private static double TriangularWave(double input)
        {
            var currentStep = (input * 4) % 2;
            var currentSign = (input % 4 >= 2) ? 1 : -1;
            if (currentStep >= 0 && currentStep < 1)
            {
                return currentStep * currentSign;
            }
            else if (currentStep >= 1 && currentStep < 2)
            {
                return 2 - currentStep * currentSign;
            }
            return 0;
        }

        public static IEnumerable<Byte> CreateSquareWaveGenerator(this WaveHeader header, int Hz, Channels channels)
        {

            WaveFunction func = context =>
            {
                var input = context.currentPosition * Hz;



                return SquareWave(input);

            };

            return GetFunctionGenerator(header, func,channels);

        }
        private static double SquareWave(double input)
        {
            var currentStep = (input * 2) % 2;
            return currentStep >= 1 ? 1 : -1;
        }

        //public static Byte[] CreateScanWaveGenerator(this WaveHeader header, Func<WaveHeader, int, IEnumerable<byte>> generatorLocator)
        //{

        //    var min = 20;
        //    var max = 20000;
        //    List<byte> results = new List<byte>(1024 ^ 2);



        //    for (int hz = min; hz <= max; hz=hz+1)
        //    {

        //        var dur = ((double)1) / hz ;
        //        var length = ((int)(dur * header.BytesPerSecond / 4)) * 4 ;
        //        var secLength = 0.02 * header.BytesPerSecond;
        //        var times = (int)(secLength / length) + 1;
        //        length = length * times;
        //        //var length = ((int)(header.BytesPerSecond * 1 / 4)) * 4;
        //        var buffer = generatorLocator(header, hz).Take(length).ToArray();
        //        results.AddRange(buffer);
        //    }


        //    //var stepsPerSec = 20;
        //    //var totalBytes = header.BytesPerSecond * duration.TotalSeconds;

        //    //var steplength = (max - min )/ (duration.TotalSeconds * stepsPerSec);
        //    //var stepDur = duration.TotalSeconds / (duration.TotalSeconds * stepsPerSec * 2);
        //    //var length = ((int) (header.BytesPerSecond * stepDur /4))  *4;

        //    //for (double hz = min; hz <= max; hz = hz + steplength)
        //    //{
        //    //    var buf = generatorLocator(header,(int)hz).Take(length).ToArray();
        //    //    results.AddRange(buf);
        //    //}

        //    //for (double hz = max; hz >20 ; hz =hz-steplength)
        //    //{
        //    //    var buf = generatorLocator(header, (int)hz).Take(length).ToArray();
        //    //    results.AddRange(buf);
        //    //}

        //    return results.ToArray();
        //}




        public static IEnumerable<Byte> GetFunctionGenerator(this WaveHeader header, WaveFunction func, Channels channels)
        {

            //foreach (var item in header.InstanceHeader)
            //{
            //    yield return item;
            //}
            var rand = new Random();


            double sampleTimeDelta = (double)1 / (double)header.SamplingFrequency;
            var channelContexts
                = Enumerable.Range(0, header.Channels)
                .Select(i => new ChannelContext() { header = header })
                .ToList();

            while (true)
                for (int i = 0; i < header.Channels; i++)
                {

                    var currentContext = channelContexts[i];
                    currentContext.lastValue = func(currentContext);


                    switch (header.SamplingDepth)
                    {
                        case 16:
                            Int16 val;
                            if ((i == 0 && channels == Channels.Right) || (i == 1 && channels == Channels.Left ))
                            {
                                val = 0;
                            }
                            else
                            {
                                val = (Int16)(Int16.MaxValue * currentContext.lastValue); 
                            }
                            byte[] bt = BitConverter.GetBytes(val);
                            yield return bt[0];
                            yield return bt[1];

                            break;
                        default:
                            throw new InvalidOperationException("Only Supported 16 bits ");

                    }


                    currentContext.currentPosition = currentContext.currentPosition + sampleTimeDelta;
                }

        }





    }
}
